// Decompiled on 周六 2月 22 19:31:49 CST 2025 with Zomboid Decompiler v0.1.3 using Vineflower.
package zombie.network.chat;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Stack;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import zombie.characters.Faction;
import zombie.characters.IsoPlayer;
import zombie.chat.ChatBase;
import zombie.chat.ChatMessage;
import zombie.chat.ChatTab;
import zombie.chat.ChatUtility;
import zombie.chat.ServerChatMessage;
import zombie.chat.defaultChats.AdminChat;
import zombie.chat.defaultChats.FactionChat;
import zombie.chat.defaultChats.GeneralChat;
import zombie.chat.defaultChats.RadioChat;
import zombie.chat.defaultChats.SafehouseChat;
import zombie.chat.defaultChats.SayChat;
import zombie.chat.defaultChats.ServerChat;
import zombie.chat.defaultChats.ShoutChat;
import zombie.chat.defaultChats.WhisperChat;
import zombie.core.Core;
import zombie.core.logger.LoggerManager;
import zombie.core.logger.ZLogger;
import zombie.core.network.ByteBufferWriter;
import zombie.core.raknet.UdpConnection;
import zombie.iso.areas.SafeHouse;
import zombie.network.PacketTypes;
import zombie.network.ServerOptions;

public class ChatServer {
    private static ChatServer instance = null;
    private static final Stack<Integer> availableChatsID = new Stack();
    private static int lastChatId = -1;
    private static final HashMap<ChatType, ChatBase> defaultChats = new HashMap();
    private static final ConcurrentHashMap<Integer, ChatBase> chats = new ConcurrentHashMap();
    private static final ConcurrentHashMap<String, FactionChat> factionChats = new ConcurrentHashMap();
    private static final ConcurrentHashMap<String, SafehouseChat> safehouseChats = new ConcurrentHashMap();
    private static AdminChat adminChat = null;
    private static GeneralChat generalChat = null;
    private static ServerChat serverChat = null;
    private static RadioChat radioChat = null;
    private static boolean inited = false;
    private static final HashSet<Short> players = new HashSet();
    private static final String logName = "chat";
    private static ZLogger logger;
    private static final HashMap<String, ChatTab> tabs = new HashMap();
    private static final String mainTabID = "main";
    private static final String adminTabID = "admin";

    public static ChatServer getInstance() {
        if (instance == null) {
            instance = new ChatServer();
        }

        return instance;
    }

    public static boolean isInited() {
        return inited;
    }

    private ChatServer() {
    }

    public void init() {
        if (!inited) {
            LoggerManager.createLogger("chat", Core.bDebug);
            logger = LoggerManager.getLogger("chat");
            logger.write("Start chat server initialization...", "info");
            ChatTab chatTab0 = new ChatTab((short)0, "UI_chat_main_tab_title_id");
            ChatTab chatTab1 = new ChatTab((short)1, "UI_chat_admin_tab_title_id");
            boolean _boolean = ServerOptions.getInstance().DiscordEnable.getValue();
            GeneralChat generalChatx = new GeneralChat(this.getNextChatID(), chatTab0, _boolean);
            SayChat sayChat = new SayChat(this.getNextChatID(), chatTab0);
            ShoutChat shoutChat = new ShoutChat(this.getNextChatID(), chatTab0);
            RadioChat radioChatx = new RadioChat(this.getNextChatID(), chatTab0);
            AdminChat adminChatx = new AdminChat(this.getNextChatID(), chatTab1);
            ServerChat serverChatx = new ServerChat(this.getNextChatID(), chatTab0);
            chats.put(generalChatx.getID(), generalChatx);
            chats.put(sayChat.getID(), sayChat);
            chats.put(shoutChat.getID(), shoutChat);
            chats.put(radioChatx.getID(), radioChatx);
            chats.put(adminChatx.getID(), adminChatx);
            chats.put(serverChatx.getID(), serverChatx);
            defaultChats.put(generalChatx.getType(), generalChatx);
            defaultChats.put(sayChat.getType(), sayChat);
            defaultChats.put(shoutChat.getType(), shoutChat);
            defaultChats.put(serverChatx.getType(), serverChatx);
            defaultChats.put(radioChatx.getType(), radioChatx);
            tabs.put("main", chatTab0);
            tabs.put("admin", chatTab1);
            generalChat = generalChatx;
            adminChat = adminChatx;
            serverChat = serverChatx;
            radioChat = radioChatx;
            inited = true;
            logger.write("General chat has id = " + generalChatx.getID(), "info");
            logger.write("Say chat has id = " + sayChat.getID(), "info");
            logger.write("Shout chat has id = " + shoutChat.getID(), "info");
            logger.write("Radio chat has id = " + radioChatx.getID(), "info");
            logger.write("Admin chat has id = " + adminChatx.getID(), "info");
            logger.write("Server chat has id = " + serverChat.getID(), "info");
            logger.write("Chat server successfully initialized", "info");
        }
    }

    public void initPlayer(short playerID) {
        logger.write("Player with id = '" + playerID + "' tries to connect", "info");
        synchronized (players) {
            if (players.contains(playerID)) {
                logger.write("Player already connected!", "warning");
                return;
            }
        }

        logger.write("Adding player '" + playerID + "' to chat server", "info");
        IsoPlayer isoPlayer = ChatUtility.findPlayer(playerID);
        UdpConnection udpConnection = ChatUtility.findConnection(playerID);
        if (udpConnection != null && isoPlayer != null) {
            this.sendInitPlayerChatPacket(udpConnection);
            this.addDefaultChats(playerID);
            logger.write("Player joined to default chats", "info");
            if (udpConnection.accessLevel == 32) {
                this.joinAdminChat(playerID);
            }

            Faction faction = Faction.getPlayerFaction(isoPlayer);
            if (faction != null) {
                this.addMemberToFactionChat(faction.getName(), playerID);
            }

            SafeHouse safeHouse = SafeHouse.hasSafehouse(isoPlayer);
            if (safeHouse != null) {
                this.addMemberToSafehouseChat(safeHouse.getId(), playerID);
            }

            ByteBufferWriter byteBufferWriter = udpConnection.startPacket();
            PacketTypes.PacketType.PlayerConnectedToChat.doPacket(byteBufferWriter);
            PacketTypes.PacketType.PlayerConnectedToChat.send(udpConnection);
            synchronized (players) {
                players.add(playerID);
            }

            logger.write("Player " + isoPlayer.getUsername() + "(" + playerID + ") joined to chat server successfully", "info");
        } else {
            logger.write("Player or connection is not found on server!", "error");
            logger.write((udpConnection == null ? "connection = null " : "") + (isoPlayer == null ? "player = null" : ""), "error");
        }
    }

    public void processMessageFromPlayerPacket(ByteBuffer bb) {
        int _int = bb.getInt();
        synchronized (chats) {
            ChatBase chatBase = (ChatBase)chats.get(_int);
            ChatMessage chatMessage = chatBase.unpackMessage(bb);
            logger.write("Got message:" + chatMessage, "info");
            if (!ChatUtility.chatStreamEnabled(chatBase.getType())) {
                logger.write("Message ignored by server because the chat disabled by server settings", "warning");
            } else {
                this.sendMessage(chatMessage);
                logger.write("Message " + chatMessage + " sent to chat (id = " + chatBase.getID() + ") members", "info");
            }
        }
    }

    public void processPlayerStartWhisperChatPacket(ByteBuffer bb) {
        logger.write("Whisper chat starting...", "info");
        if (!ChatUtility.chatStreamEnabled(ChatType.whisper)) {
            logger.write("Message for whisper chat is ignored because whisper chat is disabled by server settings", "info");
        } else {
            String string0 = zombie.GameWindow.ReadString(bb);
            String string1 = zombie.GameWindow.ReadString(bb);
            logger.write("Player '" + string0 + "' attempt to start whispering with '" + string1 + "'", "info");
            IsoPlayer isoPlayer0 = ChatUtility.findPlayer(string0);
            IsoPlayer isoPlayer1 = ChatUtility.findPlayer(string1);
            if (isoPlayer0 == null) {
                logger.write("Player '" + string0 + "' is not found!", "error");
                throw new RuntimeException("Player not found");
            } else if (isoPlayer1 == null) {
                logger.write("Player '" + string0 + "' attempt to start whisper dialog with '" + string1 + "' but this player not found!", "info");
                UdpConnection udpConnection = ChatUtility.findConnection(isoPlayer0.getOnlineID());
                this.sendPlayerNotFoundMessage(udpConnection, string1);
            } else {
                logger.write("Both players found", "info");
                WhisperChat whisperChat = new WhisperChat(this.getNextChatID(), (ChatTab)tabs.get("main"), string0, string1);
                whisperChat.addMember(isoPlayer0.getOnlineID());
                whisperChat.addMember(isoPlayer1.getOnlineID());
                chats.put(whisperChat.getID(), whisperChat);
                logger.write(
                    "Whisper chat (id = " + whisperChat.getID() + ") between '" + isoPlayer0.getUsername() + "' and '" + isoPlayer1.getUsername() + "' started",
                    "info"
                );
            }
        }
    }

    private void sendPlayerNotFoundMessage(UdpConnection udpConnection, String string) {
        ByteBufferWriter byteBufferWriter = udpConnection.startPacket();
        PacketTypes.PacketType.PlayerNotFound.doPacket(byteBufferWriter);
        byteBufferWriter.putUTF(string);
        PacketTypes.PacketType.PlayerNotFound.send(udpConnection);
        logger.write("'Player not found' packet was sent", "info");
    }

    public ChatMessage unpackChatMessage(ByteBuffer bb) {
        int _int = bb.getInt();
        return ((ChatBase)chats.get(_int)).unpackMessage(bb);
    }

    public void disconnectPlayer(short playerID) {
        logger.write("Player " + playerID + " disconnecting...", "info");
        synchronized (chats) {
            for (ChatBase chatBase : chats.values()) {
                chatBase.removeMember(playerID);
                if (chatBase.getType() == ChatType.whisper) {
                    this.closeChat(chatBase.getID());
                }
            }
        }

        synchronized (players) {
            players.remove(playerID);
        }

        logger.write("Disconnecting player " + playerID + " finished", "info");
    }

    private void closeChat(int _int) {
        synchronized (chats) {
            if (!chats.containsKey(_int)) {
                throw new RuntimeException("Chat '" + _int + "' requested to close but it's not exists.");
            }

            ChatBase chatBase = (ChatBase)chats.get(_int);
            chatBase.close();
            chats.remove(_int);
        }

        synchronized (availableChatsID) {
            availableChatsID.push(_int);
        }
    }

    public void joinAdminChat(short playerID) {
        if (adminChat == null) {
            logger.write("Admin chat is null! Can't add player to it", "warning");
        } else {
            adminChat.addMember(playerID);
            logger.write("Player joined admin chat", "info");
        }
    }

    public void leaveAdminChat(short playerID) {
        logger.write("Player " + playerID + " are leaving admin chat...", "info");
        UdpConnection udpConnection = ChatUtility.findConnection(playerID);
        if (adminChat == null) {
            logger.write("Admin chat is null. Can't leave it! ChatServer", "warning");
        } else if (udpConnection == null) {
            logger.write("Connection to player is null. Can't leave admin chat! ChatServer.leaveAdminChat", "warning");
        } else {
            adminChat.leaveMember(Short.valueOf(playerID));
            ((ChatTab)tabs.get("admin")).sendRemoveTabPacket(udpConnection);
            logger.write("Player " + playerID + " leaved admin chat", "info");
        }
    }

    public FactionChat createFactionChat(String name) {
        logger.write("Creating faction chat '" + name + "'", "info");
        if (factionChats.containsKey(name)) {
            logger.write("Faction chat '" + name + "' already exists!", "warning");
            return (FactionChat)factionChats.get(name);
        } else {
            FactionChat factionChat = new FactionChat(this.getNextChatID(), (ChatTab)tabs.get("main"));
            chats.put(factionChat.getID(), factionChat);
            factionChats.put(name, factionChat);
            logger.write("Faction chat '" + name + "' created", "info");
            return factionChat;
        }
    }

    public SafehouseChat createSafehouseChat(String safehouseID) {
        logger.write("Creating safehouse chat '" + safehouseID + "'", "info");
        if (safehouseChats.containsKey(safehouseID)) {
            logger.write("Safehouse chat already has chat with name '" + safehouseID + "'", "warning");
            return (SafehouseChat)safehouseChats.get(safehouseID);
        } else {
            SafehouseChat safehouseChat = new SafehouseChat(this.getNextChatID(), (ChatTab)tabs.get("main"));
            chats.put(safehouseChat.getID(), safehouseChat);
            safehouseChats.put(safehouseID, safehouseChat);
            logger.write("Safehouse chat '" + safehouseID + "' created", "info");
            return safehouseChat;
        }
    }

    public void removeFactionChat(String factionName) {
        logger.write("Removing faction chat '" + factionName + "'...", "info");
        int _int;
        synchronized (factionChats) {
            if (!factionChats.containsKey(factionName)) {
                String string = "Faction chat '" + factionName + "' tried to delete but it's not exists.";
                logger.write(string, "error");
                RuntimeException runtimeException = new RuntimeException(string);
                logger.write(runtimeException);
                throw runtimeException;
            }

            FactionChat factionChat = (FactionChat)factionChats.get(factionName);
            _int = factionChat.getID();
            factionChats.remove(factionName);
        }

        this.closeChat(_int);
        logger.write("Faction chat '" + factionName + "' removed", "info");
    }

    public void removeSafehouseChat(String safehouseName) {
        logger.write("Removing safehouse chat '" + safehouseName + "'...", "info");
        int _int;
        synchronized (safehouseChats) {
            if (!safehouseChats.containsKey(safehouseName)) {
                String string = "Safehouse chat '" + safehouseName + "' tried to delete but it's not exists.";
                logger.write(string, "error");
                RuntimeException runtimeException = new RuntimeException(string);
                logger.write(runtimeException);
                throw runtimeException;
            }

            SafehouseChat safehouseChat = (SafehouseChat)safehouseChats.get(safehouseName);
            _int = safehouseChat.getID();
            safehouseChats.remove(safehouseName);
        }

        this.closeChat(_int);
        logger.write("Safehouse chat '" + safehouseName + "' removed", "info");
    }

    public void syncFactionChatMembers(String factionName, String factionOwner, ArrayList<String> _players) {
        logger.write("Start syncing faction chat '" + factionName + "'...", "info");
        if (factionName != null && factionOwner != null && _players != null) {
            synchronized (factionChats) {
                if (!factionChats.containsKey(factionName)) {
                    logger.write("Faction chat '" + factionName + "' is not exist", "warning");
                    return;
                }

                ArrayList arrayList = new ArrayList(_players);
                arrayList.add(factionOwner);
                FactionChat factionChat = (FactionChat)factionChats.get(factionName);
                factionChat.syncMembersByUsernames(arrayList);
                StringBuilder stringBuilder = new StringBuilder("These members were added: ");

                for (short short0 : factionChat.getJustAddedMembers()) {
                    stringBuilder.append("'").append(ChatUtility.findPlayerName(short0)).append("', ");
                }

                stringBuilder.append(". These members were removed: ");

                for (short short1 : factionChat.getJustRemovedMembers()) {
                    stringBuilder.append("'").append(ChatUtility.findPlayerName(short1)).append("', ");
                }

                logger.write(stringBuilder.toString(), "info");
            }

            logger.write("Syncing faction chat '" + factionName + "' finished", "info");
        } else {
            logger.write("Faction name or faction owner or players is null", "warning");
        }
    }

    public void syncSafehouseChatMembers(String safehouseID, String safehouseOwner, ArrayList<String> _players) {
        logger.write("Start syncing safehouse chat '" + safehouseID + "'...", "info");
        if (safehouseID != null && safehouseOwner != null && _players != null) {
            synchronized (safehouseChats) {
                if (!safehouseChats.containsKey(safehouseID)) {
                    logger.write("Safehouse chat '" + safehouseID + "' is not exist", "warning");
                    return;
                }

                ArrayList arrayList = new ArrayList(_players);
                arrayList.add(safehouseOwner);
                SafehouseChat safehouseChat = (SafehouseChat)safehouseChats.get(safehouseID);
                safehouseChat.syncMembersByUsernames(arrayList);
                StringBuilder stringBuilder = new StringBuilder("These members were added: ");

                for (short short0 : safehouseChat.getJustAddedMembers()) {
                    stringBuilder.append("'").append(ChatUtility.findPlayerName(short0)).append("', ");
                }

                stringBuilder.append("These members were removed: ");

                for (short short1 : safehouseChat.getJustRemovedMembers()) {
                    stringBuilder.append("'").append(ChatUtility.findPlayerName(short1)).append("', ");
                }

                logger.write(stringBuilder.toString(), "info");
            }

            logger.write("Syncing safehouse chat '" + safehouseID + "' finished", "info");
        } else {
            logger.write("Safehouse name or Safehouse owner or players is null", "warning");
        }
    }

    private void addMemberToSafehouseChat(String string, short _short) {
        if (!safehouseChats.containsKey(string)) {
            logger.write("Safehouse chat is not initialized!", "warning");
        } else {
            synchronized (safehouseChats) {
                SafehouseChat safehouseChat = (SafehouseChat)safehouseChats.get(string);
                safehouseChat.addMember(_short);
            }

            logger.write("Player joined to chat of safehouse '" + string + "'", "info");
        }
    }

    private void addMemberToFactionChat(String string, short _short) {
        if (!factionChats.containsKey(string)) {
            logger.write("Faction chat is not initialized!", "warning");
        } else {
            synchronized (factionChats) {
                FactionChat factionChat = (FactionChat)factionChats.get(string);
                factionChat.addMember(_short);
            }

            logger.write("Player joined to chat of faction '" + string + "'", "info");
        }
    }

    public void sendServerAlertMessageToServerChat(String author, String msg) {
        serverChat.sendMessageToChatMembers(serverChat.createMessage(author, msg, true));
        logger.write("Server alert message: '" + msg + "' by '" + author + "' sent.");
    }

    public void sendServerAlertMessageToServerChat(String msg) {
        serverChat.sendMessageToChatMembers(serverChat.createServerMessage(msg, true));
        logger.write("Server alert message: '" + msg + "' sent.");
    }

    public ChatMessage createRadiostationMessage(String text, int radioChannel) {
        return radioChat.createBroadcastingMessage(text, radioChannel);
    }

    public void sendMessageToServerChat(UdpConnection connection, String msg) {
        ServerChatMessage serverChatMessage = serverChat.createServerMessage(msg, false);
        serverChat.sendMessageToPlayer(connection, serverChatMessage);
    }

    public void sendMessageToServerChat(String msg) {
        ServerChatMessage serverChatMessage = serverChat.createServerMessage(msg, false);
        serverChat.sendMessageToChatMembers(serverChatMessage);
    }

    public void sendMessageFromDiscordToGeneralChat(String author, String msg) {
        if (author != null && msg != null) {
            logger.write("Got message '" + msg + "' by author '" + author + "' from discord");
        }

        ChatMessage chatMessage = generalChat.createMessage(msg);
        chatMessage.makeFromDiscord();
        chatMessage.setAuthor(author);
        if (ChatUtility.chatStreamEnabled(ChatType.general)) {
            this.sendMessage(chatMessage);
            logger.write("Message '" + msg + "' send from discord to general chat members");
        } else {
            generalChat.sendToDiscordGeneralChatDisabled();
            logger.write("General chat disabled so error message sent to discord", "warning");
        }
    }

    private int getNextChatID() {
        synchronized (availableChatsID) {
            if (availableChatsID.isEmpty()) {
                lastChatId++;
                availableChatsID.push(lastChatId);
            }

            return (Integer)availableChatsID.pop();
        }
    }

    private void sendMessage(ChatMessage chatMessage) {
        synchronized (chats) {
            if (chats.containsKey(chatMessage.getChatID())) {
                ChatBase chatBase = (ChatBase)chats.get(chatMessage.getChatID());
                chatBase.sendMessageToChatMembers(chatMessage);
            }
        }
    }

    private void sendInitPlayerChatPacket(UdpConnection udpConnection) {
        ByteBufferWriter byteBufferWriter = udpConnection.startPacket();
        PacketTypes.PacketType.InitPlayerChat.doPacket(byteBufferWriter);
        byteBufferWriter.putShort((short)tabs.size());

        for (ChatTab chatTab : tabs.values()) {
            byteBufferWriter.putShort(chatTab.getID());
            byteBufferWriter.putUTF(chatTab.getTitleID());
        }

        PacketTypes.PacketType.InitPlayerChat.send(udpConnection);
    }

    private void addDefaultChats(short _short) {
        for (Entry entry : defaultChats.entrySet()) {
            ChatBase chatBase = (ChatBase)entry.getValue();
            chatBase.addMember(_short);
        }
    }

    public void sendMessageToAdminChat(String msg) {
        ServerChatMessage serverChatMessage = adminChat.createServerMessage(msg);
        adminChat.sendMessageToChatMembers(serverChatMessage);
    }
}
